#pragma once

#include "p13.h"
#include <iostream>
#include <string>
#include <string_view>
#include <queue>
#include <utility>
#include <chrono>
#include <random>


// 对于 class 而言，所有非静态成员函数都有一个thisconst 指针，用于保存隐式对象的地址。
// 为什么this是指针而不是引用。答案很简单：当它this被添加到 C++ 时，引用还不存在。
// 由于this指针始终指向隐式对象（除非我们做了一些导致未定义行为的事情，否则永远不会是空指针）
namespace HOMEWORK_P15 {
    class Calc {
    public:
        Calc& add(int value) { mValue += value; return *this; }
        Calc& sub(int value) { mValue -= value; return *this; }
        Calc& mult(int value) { mValue *= value; return *this; }

        int getValue() const { return mValue; }

        // 让该函数创建一个新对象（使用默认构造函数），然后将该新对象分配给当前隐式对象
        void reset() { *this = {}; }
    private:
        int mValue {};
    };


    void testThisPtr();
    void testResetThisInstance();

    class Simple {
    private:
        int m_id {1};
        int data {};
    public:
        Simple(int id) : m_id{id} {}

        int getId() const { return m_id; }
        void setId(int id) { m_id = id; }

        // 如果遇到同名成员变量，需要加上 this 作为区分
        void setData(int data) { this->data = data;}

        // 初识 this ptr.
        void print() const { std::cout << "this->m_id: " << this->m_id << "m_id: " << m_id << '\n'; }
    };

    enum class FruitType
    {
        apple,
        banana,
        cherry
    };


    class Fruit
    {
    private:
        FruitType m_type {};
        int m_percentageEaten {0};
    public:
        Fruit(FruitType type) : m_type {type} { }
        FruitType getType() { return m_type; }
        int getPercentageEaten() { return m_percentageEaten; }
        bool isCherry() { return m_type == FruitType::cherry; }
    };

    class FruitNew
    {
    public:
        enum Type {
            apple,
            banana,
            cherry
        };

    private:
        Type m_type {};
        int m_percentageEaten {0};
    public:
        FruitNew(Type type) : m_type { type } {}
        Type getType() const { return m_type; }
        int getPercentageEaten() { return m_percentageEaten; }
        bool isCherry() { return m_type == Type::cherry; }
    };

    class Empployee
    {
    public:
        using IDType = int;

        class Printer
        {
        public:
            void print(const Empployee& e) const {
                std::cout << ' ' << e.m_name << " has id: " << e.m_id << " and wage: " << e.m_wage << '\n';
            }
        };

    private:
        std::string m_name {};
        IDType m_id {};
        double m_wage {};

    public:
        Empployee(std::string_view name, IDType id, double wage)
            : m_name {name}
            , m_id {id}
            , m_wage {wage} {}

        const std::string& getName() const { return m_name; }
        IDType getId() const { return m_id; }
    };

    void test_chap_15_3();

    // Learn destructor
    class NetWorkData
    {
    private:
        std::string m_serverName {};
        std::queue<std::string> m_dataQueue {};
    public:
        NetWorkData(std::string_view server_name) : m_serverName(server_name) {}
        ~NetWorkData() {
            sendData();
        }

        void addData(std::string data) {
            m_dataQueue.push(data);
        }

        void sendData() {
            std::cout << " Send Data to " << m_serverName << '\n';
            while (!m_dataQueue.empty()) {
                std::cout << " Front elem is " << m_dataQueue.front() << '\n';
                m_dataQueue.pop();
            }
        }
    };

    void test_chap_15_4();

    template <typename T>
    class Pair {
    private:
        T m_first {};
        T m_second {};
    public:
        Pair(const T& first, const T& second)
            : m_first(first)
            , m_second(second) {}

        bool isEqual(const Pair<T>& pair);
    };

    template <typename T>
    bool Pair<T>::isEqual(const Pair<T>& pair) {
        return m_first == pair.m_first && m_second == pair.m_second;
    }

    void test_chap_15_5();

    template <typename T, typename U, typename D>
    class Triad {
    private:
        T m_first {};
        U m_second {};
        D m_third {};
    public:
        Triad(const T& first, const U& second, const D& third)
            : m_first(first)
            , m_second(second)
            , m_third(third) {}

        // Getter
        T getFirst() const { return m_first; }
        U getSecond() const { return m_second; }
        D getThird() const { return m_third; }

        // Setter
        void setFirst(const T& first) {
            m_first = first;
        }

        void setSecond(const U& second) {
            m_second = second;
        }

        void setThird(const D& third) {
            m_third = third;
        }

        void print() const;
    };

    template <typename T, typename U, typename D>
    void Triad<T, U, D>::print() const {
        std::cout << '[' << m_first << ", " << m_second << ", " << m_third << ']';
    }

    class Something {
    private:
        int m_id {};
        static inline auto m_x { 5 };
        static inline std::pair m_v {1, 2.3};
        static inline int s_idGenerator { 1 };
    public:
        static constexpr double s_value { 4.0 };
        static constexpr std::string_view s_view { "Hello" };

        Something() : m_id { s_idGenerator++ } {}

        int getId() const { return m_id; }

        void print() {
            std::cout << s_view << ", This value: " << s_value << ", obj value: " << m_id << '\n';
            std::cout << s_view << ", m_x: " << m_x << ", pair: " << m_v.first << ", " << m_v.second << '\n';
        }
    };

    void test_chap_15_6();

    class Random {
    private:
        static inline std::mt19937 generate() {
            std::random_device rd{};

            // Create seed_seq with high-res clock and 7 random numbers from std::random_device
            std::seed_seq ss{
                static_cast<std::seed_seq::result_type>(std::chrono::steady_clock::now().time_since_epoch().count()),
                    rd(), rd(), rd(), rd(), rd(), rd(), rd() };

            return std::mt19937{ ss };
        }
        static inline std::mt19937 mt { generate() };
    public:
        static inline int get(int min, int max) {
            return std::uniform_int_distribution{min, max}(mt);
        }
    };

    void test_chap_15_7();

    class Accumulator {
    private:
        int m_value { 0 };

    public:
        void add(int value) { m_value += value; }

        friend void print(const Accumulator& accumulator) {
            std::cout << " Member value: " << accumulator.m_value << '\n';
        }
    };

    class Humidity;

    class Temperature {
    private:
        int m_temp { 0 };
    public:
        explicit Temperature(int temp)
            : m_temp{temp} {}

        friend void printWeather(const Temperature& temperature, const Humidity& humidity);
    };

    class Humidity {
    private:
        int m_humidity { 0 };
    public:
        explicit Humidity(int humidity)
        : m_humidity{ humidity } {}

        friend void printWeather(const Temperature& temperature, const Humidity& humidity);
    };


    void test_chap_15_8();

    class Storage {
    private:
        int m_nValue {};
        double m_dValue {};
    public:
        Storage(int nValue, double dValue)
            : m_nValue {nValue}
            , m_dValue {dValue} {}

        // 创建一个友缘类
        friend class Display;
    };

    class Display {
    private:
        bool m_displayIntFirst {};
    public:
        Display(bool displayIntFirst)
            : m_displayIntFirst {displayIntFirst} {}

        // 因为Display 是一个storage的友元，因此display可以获取Storage的成员函数
        void displayStorage(const Storage& storage) {
            if (m_displayIntFirst) {
                std::cout << storage.m_nValue << ' ' << storage.m_dValue << '\n';
            } else {
                std::cout << storage.m_dValue << ' ' << storage.m_nValue << '\n';
            }
        }

        void setDisplayIntFirst(bool b) {
            m_displayIntFirst = b;
        }
    };


    class StorageNew;

    class DisplayNew {
    private:
        bool m_displayIntFirst {};
    public:
        DisplayNew(bool displayIntFirst)
            : m_displayIntFirst {displayIntFirst} {}

        // 因为Display 是一个storage的友元，因此display可以获取Storage的成员函数
        void displayStorageNew(const StorageNew& storage);

        void setDisplayIntFirst(bool b) {
            m_displayIntFirst = b;
        }
    };

    class StorageNew {
    private:
        int m_nValue {};
        double m_dValue {};
    public:
        StorageNew(int nValue, double dValue)
            : m_nValue {nValue}
            , m_dValue {dValue} {}

        // 创建一个友缘类
        friend void DisplayNew::displayStorageNew(const StorageNew& storage);
    };

    void test_chap_15_9();

    class Vector3d {
    private:
        double m_pt_x {};
        double m_pt_y {};
        double m_pt_z {};
    public:
        Vector3d(double x, double y, double z)
            : m_pt_x(x)
            , m_pt_y(y)
            , m_pt_z(z) {}

        void print() const {
            std::cout << "Vector(" << m_pt_x << ", " << m_pt_y << ", " << m_pt_z << ")\n";
        }

        friend class Point3d;
    };

    class Point3d {
    private:
        double m_pt_x {};
        double m_pt_y {};
        double m_pt_z {};
    public:
        Point3d(double x, double y, double z)
            : m_pt_x {x}
            , m_pt_y {y}
            , m_pt_z {z} {}

        void print() const {
            std::cout << "Point3d(" << m_pt_x << ", " << m_pt_y << ", " << m_pt_z << ")\n";
        }

        void moveByVector(const Vector3d& v) {
            m_pt_x += v.m_pt_x;
            m_pt_y += v.m_pt_y;
            m_pt_z += v.m_pt_z;
        }
    };

    void test_chap_15_9_homework();
}

namespace HOMEWORK_P15_9_2 {

    class Vector3d;

    class Point3d {
    private:
        double m_pt_x {};
        double m_pt_y {};
        double m_pt_z {};
    public:
        Point3d(double x, double y, double z)
            : m_pt_x {x}
            , m_pt_y {y}
            , m_pt_z {z} {}

        void print() const {
            std::cout << "Point3d(" << m_pt_x << ", " << m_pt_y << ", " << m_pt_z << ")\n";
        }

        void moveByVector(const Vector3d& v);
    };

    class Vector3d {
    private:
        double m_pt_x {};
        double m_pt_y {};
        double m_pt_z {};
    public:
        Vector3d(double x, double y, double z)
            : m_pt_x(x)
            , m_pt_y(y)
            , m_pt_z(z) {}

        void print() const {
            std::cout << "Vector(" << m_pt_x << ", " << m_pt_y << ", " << m_pt_z << ")\n";
        }

        friend void Point3d::moveByVector(const Vector3d& v);
    };

    void test_chap_15_9_homework();

}


namespace HOMEWORK_P15_10 {

    class Employee {
    private:
        std::string m_name {};
    public:
        Employee(std::string_view name)
            : m_name {name} {}

        const std::string& getName() const & { return m_name; }
        std::string getName() const && { return m_name; }
    };

    Employee createEmployee(std::string_view name);
    void test_reference();
}


namespace HOMEWORK_P15_SUMMARY_TEST {

    // enum class MonsterType {
    //     Dragon,
    //     Goblin,
    //     Ogre,
    //     Orc,
    //     Skeleton,
    //     Troll,
    //     Vampire,
    //     Zombie,
    //     maxMonsterTypes
    // };

    class Monster {
    public:
        enum Type {
            Dragon,
            Goblin,
            Ogre,
            Orc,
            Skeleton,
            Troll,
            Vampire,
            Zombie,
            maxMonsterTypes
        };

    private:
        Type m_type {};
        std::string m_name {"???"};
        std::string m_roar {"???"};
        int m_numHitPoint {};

    public:
        Monster(Type type, const std::string& name, const std::string& roar, int numHitPoint)
            : m_type {type}
            , m_name {name}
            , m_roar {roar}
            , m_numHitPoint(numHitPoint) {}

        constexpr std::string_view getTypeString() const {
            std::string_view typeName;
            if (m_type == Monster::Dragon) {
                typeName = "Dragon";
            } else if (m_type == Monster::Goblin) {
                typeName = "Goblin";
            } else if (m_type == Monster::Ogre) {
                typeName = "Ogre";
            } else if (m_type == Monster::Orc) {
                typeName = "Orc";
            } else if (m_type == Monster::Skeleton) {
                typeName = "Skeleton";
            } else if (m_type == Monster::Troll) {
                typeName = "Troll";
            } else if (m_type == Monster::Vampire) {
                typeName = "Vampire";
            } else if (m_type == Monster::Zombie) {
                typeName = "Zombie";
            } else {
                typeName = "???";
                std::cout << " [Warnning] Current monster type not support!" << '\n';
            }

            return typeName;
        }

        Monster generate();
        void print();
    };
    void test();
}
